package cn.trigram.date;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.util.Date;
import java.util.Objects;

/**
 * 时间调整器
 */
public class Adjuster {

  private LocalDateTime ldt;

  Adjuster(Date d) {

    Objects.requireNonNull(d, "日期参数不能为空");
    this.ldt = LocalDateTime.ofInstant(d.toInstant(), ZoneId.systemDefault());
  }

  Adjuster(LocalDateTime ldt) {

    Objects.requireNonNull(ldt, "日期参数不能为空");
    this.ldt = ldt;
  }

  public Adjuster plus(Period period, Duration duration) {

    Objects.requireNonNull(period, "周期period不能为空");
    Objects.requireNonNull(duration, "时间段duration不能为空");
    this.ldt = this.ldt.plus(period).plus(duration);
    return this;
  }

  public Adjuster plusYear(int year) {

    this.ldt = this.ldt.plus(Period.ofYears(year));
    return this;
  }

  public Adjuster plusMonth(int month) {

    this.ldt = this.ldt.plus(Period.ofMonths(month));
    return this;
  }

  public Adjuster plusDay(int day) {

    this.ldt = this.ldt.plus(Period.ofDays(day));
    return this;
  }

  public Adjuster plusHour(int hour) {

    this.ldt = this.ldt.plus(Duration.ofHours(hour));
    return this;
  }

  public Adjuster plusMinute(int minute) {

    this.ldt = this.ldt.plus(Duration.ofMinutes(minute));
    return this;
  }

  public Adjuster plusSecond(int second) {

    this.ldt = this.ldt.plus(Duration.ofSeconds(second));
    return this;
  }

  public Adjuster plusMillis(int millis) {

    this.ldt = this.ldt.plus(Duration.ofMillis(millis));
    return this;
  }

  public Adjuster plusNanos(int nanos) {

    this.ldt = this.ldt.plus(Duration.ofNanos(nanos));
    return this;
  }

  public Adjuster minus(Period period, Duration duration) {

    Objects.requireNonNull(period, "周期period不能为空");
    Objects.requireNonNull(duration, "时间段duration不能为空");
    this.ldt = this.ldt.minus(period).minus(duration);
    return this;
  }

  public Adjuster minusYear(int year) {

    this.ldt = this.ldt.minus(Period.ofYears(year));
    return this;
  }

  public Adjuster minusMonth(int month) {

    this.ldt = this.ldt.minus(Period.ofMonths(month));
    return this;
  }

  public Adjuster minusDay(int day) {

    this.ldt = this.ldt.minus(Period.ofDays(day));
    return this;
  }

  public Adjuster minusHour(int hour) {

    this.ldt = this.ldt.minus(Duration.ofHours(hour));
    return this;
  }

  public Adjuster minusMinute(int minute) {

    this.ldt = this.ldt.minus(Duration.ofMinutes(minute));
    return this;
  }

  public Adjuster minusSecond(int second) {

    this.ldt = this.ldt.minus(Duration.ofSeconds(second));
    return this;
  }

  public Adjuster minusMillis(int millis) {

    this.ldt = this.ldt.minus(Duration.ofMillis(millis));
    return this;
  }

  public Adjuster minusNanos(int nanos) {

    this.ldt = this.ldt.minus(Duration.ofNanos(nanos));
    return this;
  }


  public Adjuster resetAtStartOfYear() {

    this.ldt = this.ldt.withDayOfYear(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
    return this;
  }

  public Adjuster resetAtStartOfMonth() {

    this.ldt = this.ldt.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
    return this;
  }

  public Adjuster resetAtStartOfWeek() {

    this.ldt = this.ldt.minusDays(this.ldt.getDayOfWeek().getValue()).plusDays(1).withDayOfMonth(1).withHour(0)
        .withMinute(0).withSecond(0).withNano(0);
    return this;
  }

  public Adjuster resetAtStartOfDay() {

    this.ldt = this.ldt.withHour(0).withMinute(0).withSecond(0).withNano(0);
    return this;
  }

  public Adjuster resetAtStartOfHour() {

    this.ldt = this.ldt.withMinute(0).withSecond(0).withNano(0);
    return this;
  }

  public Adjuster resetAtStartOfMinutes() {

    this.ldt = this.ldt.withSecond(0).withNano(0);
    return this;
  }

  public Adjuster resetAtStartOfSecond() {

    this.ldt = this.ldt.withNano(0);
    return this;
  }


  public LocalDateTime end() {

    return this.ldt;
  }

}
